﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Iot.RealTimeData.RealTimeData;
using Iot.Shared.Path;
using Microsoft.Extensions.Logging;
using Prism.Mvvm;

namespace Iot.RealTimeData.WPF.Prism.ShowValue;

/// <summary>
///     WpfApplicationOnValue 是一个抽象泛型类，实现了 IShowValue 和 ISetValue 接口
///     它继承自 BindableBase，提供了基于值的 WPF 应用程序功能
/// </summary>
/// <typeparam name="T">值的类型</typeparam>
public abstract class WpfApplicationOnValue<T>(ILogger logger) : BindableBase, IShowValue<T>, ISetValue<T>
{
    // 用于线程同步的锁对象
    private readonly object _lockObj = new();

    // 存储值的字段，使用 ! 标记表示不为 null
    private T _value = default!;

    /// <summary>
    ///     更新时间
    /// </summary>
    public DateTimeOffset Timestamp { get; private set; }


    /// <summary>
    ///     设置值的方法
    /// </summary>
    /// <param name="value">要设置的值</param>
    /// <param name="timeStamp">时间戳，用于判断是否需要更新</param>
    public void SetValue(T value, DateTimeOffset timeStamp)
    {
        // 如果时间戳小于等于当前时间戳，则直接返回
        if (timeStamp <= Timestamp) return;
        // 如果存在 SetValueEvent 事件，则异步触发
        if (SetValueEvent is not null)
        {
            Task.Run(() => SetValueEvent?.Invoke(value, timeStamp)).ContinueWith(t =>
            {
                // 如果任务出错，记录错误日志
                if (t.IsFaulted)
                {
                    logger.LogError(t.Exception, "名称：{名称},路径：{路径}，函数：{函数}。设置数据失败", nameof(WpfApplicationOnValue<T>),
                        Path, nameof(SetValue));
                }
            }, TaskContinuationOptions.OnlyOnFaulted);
        }

        // 使用锁确保线程安全
        lock (_lockObj)
        {
            // 再次检查时间戳，防止在等待锁的过程中时间戳已经更新
            if (timeStamp <= Timestamp) return;
            // 更新时间戳
            Timestamp = timeStamp;
            // 如果新值与旧值相同，则直接返回
            if (EqualityComparer<T>.Default.Equals(Value, value)) return;
            // 更新值
            Value = value;
            // 如果存在 ValueChanged 事件，则异步触发
            if (ValueChanged is not null)
            {
                Task.Run(() => ValueChanged?.Invoke(value, timeStamp)).ContinueWith(t =>
                {
                    // 如果任务出错，记录错误日志
                    if (t.IsFaulted)
                    {
                        logger.LogError(t.Exception, "名称：{名称},路径：{路径}，函数：{函数}。更新数据失败",
                            nameof(WpfApplicationOnValue<T>), Path, nameof(SetValue));
                    }
                }, TaskContinuationOptions.OnlyOnFaulted);
            }
        }
    }

    /// <summary>
    ///     当前值
    /// </summary>
    public T Value
    {
        get => _value;
        private set => SetProperty(ref _value, value);
    }

    /// <summary>
    ///     设备路径，使用 required 修饰符确保在对象创建时必须初始化
    /// </summary>
    public required DevicePath Path { get; init; }

    /// <summary>
    ///     值变更事件
    /// </summary>
    public event Action<T, DateTimeOffset>? ValueChanged;

    /// <summary>
    ///     设置值事件
    /// </summary>
    public event Action<T, DateTimeOffset>? SetValueEvent;
}